package org.bjf.modules.core.web.advice;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.bjf.exception.BaseException;
import org.bjf.exception.FatalException;
import org.bjf.exception.MsgCode;
import org.bjf.modules.core.web.core.LiveResp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.sql.SQLException;

import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.bjf.exception.CommMsgCode.*;

/**
 * 全局异常处理
 */

@RestControllerAdvice
@Slf4j
public class ExceptionAdvice {

  private static final Logger logEmail = LoggerFactory.getLogger("email");

  /**
   * 405 not support异常
   */
  @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class,
      HttpMediaTypeNotSupportedException.class})
  @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
  public LiveResp onNotSupportedException(Exception e,
      HttpServletRequest request) {
    String uri = request.getRequestURI();
    log.error("uri:{},code:{},message:{}", uri, NOT_SUPPORTED.getCode(), e.getMessage());

    return createErrorResp(NOT_SUPPORTED, null);
  }

  /**
   * 404 not support异常
   */
  @ExceptionHandler(value = NoHandlerFoundException.class)
  @ResponseStatus(HttpStatus.NOT_FOUND)
  public LiveResp onException(NoHandlerFoundException e, HttpServletRequest request) {
    String uri = request.getRequestURI();
    log.error("uri:{},code:{},message:{}", uri, NOT_FOUND.getCode(), e.getMessage());

    return createErrorResp(NOT_FOUND, null);
  }

  @ExceptionHandler(value = Exception.class)
  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  public LiveResp onException(Exception e, HttpServletRequest request) {
    String uri = request.getRequestURI();
    String params = JSONObject.toJSONString(request.getParameterMap());

    if (e instanceof SQLException || e instanceof DataAccessException) {
      createLog(e, uri, params);
      return createErrorResp(DB_ERROR, null);
    } else if (e instanceof FatalException) {
      FatalException ex = (FatalException) e;
      // 异步发送邮件
      logEmail.error(ex.getMessage(), ex);
      createLog(e, uri, params);
      return createErrorResp(ex.getMsgCode(), ex.getMessage());
    } else if (e instanceof BaseException) {
      BaseException ex = (BaseException) e;
      log.error("uri:{},params:{},code:{},message:{}", uri, params, ex.getMsgCode().getCode(),
          ex.getMessage());
      return createErrorResp(ex.getMsgCode(), ex.getMessage());
    } else if (e instanceof MissingServletRequestParameterException
        || e instanceof BindException
        || e instanceof ConstraintViolationException
        || e instanceof TypeMismatchException
        || e instanceof ServletRequestBindingException) {
      createLog(e, uri, params);
      return createErrorResp(PARAM_ERROR, null);
    } else {
      createLog(e, uri, params);
      return createErrorResp(SERVER_ERROR, null);
    }
  }

  private LiveResp createErrorResp(MsgCode msgCode, String message) {
    return new LiveResp(msgCode.getCode(), isNotBlank(message) ? message : msgCode.getMessage());
  }

  private void createLog(Exception e, String uri, String params) {
    log.error("uri:" + uri + ",params:" + params, e);
  }

}
